﻿using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using ActivateYourGlutes.Data;
using ActivateYourGlutes.Infrastructure;
using ActivateYourGlutes.Services.Model;
using AutoMapper;
using Goal = ActivateYourGlutes.Data.Model.Goal;
using Measurement = ActivateYourGlutes.Data.Model.Measurement;

namespace ActivateYourGlutes.Services.Implementation
{
    public class MeasurementTypeProvider : ProviderBase<Data.Model.MeasurementType>, IMeasurementTypeProvider
    {
        public MeasurementTypeProvider(IEntityModelFactory factory) : base(factory)
        {
        }

        /// <summary>
        /// Returns a list of the measurement types that can be logged against
        /// </summary>
        public List<MeasurementType> MeasurementTypes(Account forAccount)
        {
            var measurementTypes = Repository.EagerlyLoadedEntities("Owner")
                .Where(mt => mt.Owner == null || mt.Owner.AccountId == forAccount.AccountId)
                .ToList();
            return Mapper.Map<List<Data.Model.MeasurementType>, List<MeasurementType>>(measurementTypes);
        }

        /// <summary>
        /// Returns the value types that a meaurement can be taken with
        /// </summary>
        public List<MeasurementTypeValueType> ValueTypes()
        {
            return MeasurementTypeValueType.All();
        }

        /// <summary>
        /// Adds the given measurement type to database and assigns it to the account
        /// </summary>
        public void AddToAccount(Account account, MeasurementType measurementType)
        {
            var internalMeasurementType = Mapper.Map<MeasurementType, Data.Model.MeasurementType>(measurementType);
            NameValueCollection errors = internalMeasurementType.Validate();
            if (errors.Count > 0)
                throw new ValidationException(errors);
            // check for duplicates
            var duplicate = Repository.Entities
                .Where(
                mt => (mt.Owner == null || mt.Owner.AccountId == account.AccountId) &&
                      mt.Description.ToLower() == internalMeasurementType.Description.ToLower()).Any();
            if (duplicate)
                throw new ValidationException("_FORM", "You already have that measurement type.");
            internalMeasurementType.DisplayOrder = Repository.Entities
                                                       .Where(
                                                       mt =>
                                                       mt.Owner == null ||
                                                       mt.Owner.AccountId == account.AccountId)
                                                       .Max(mt => mt.DisplayOrder) + 1;
            internalMeasurementType.OwnerId = account.AccountId;
            Repository.Create(internalMeasurementType);
            Repository.SaveChanges();
        }

        /// <summary>
        /// Gets an account by ID
        /// </summary>
        public MeasurementType GetById(Account accessingAccount, int id)
        {
            var measurementType = Repository.EagerlyLoadedEntities("Owner").Where(mt => mt.MeasurementTypeId == id).First();
            if (measurementType.Owner != null && measurementType.Owner.AccountId != accessingAccount.AccountId)
                throw new AuthorizationException("You do not have rights to view this measurement type");
            return Mapper.Map<Data.Model.MeasurementType, MeasurementType>(measurementType);
        }

        /// <summary>
        /// Deletes a measurement type identified by id
        /// </summary>
        public void Delete(Account accessingAccount, int id)
        {
            var measurementType = Repository.EagerlyLoadedEntities("Owner",
                "Owner.PrimaryMeasurementType",
                "Owner.SecondaryMeasurementType",
                "Measurements",
                "Goals",
                "Measurements.Body")
                .Where(mt => mt.MeasurementTypeId == id)
                .First();

            if ((measurementType.Owner != null && measurementType.Owner.AccountId != accessingAccount.AccountId) || measurementType.Owner == null)
                throw new AuthorizationException("You do not have rights to view this measurement type");

            var measurementRepository = GetRepository<Measurement>();
            var goalRepository = GetRepository<Goal>();

            foreach(var measurement in measurementType.Measurements.ToList())
            {
                measurementRepository.Delete(measurement);
            }

            foreach(var goal in measurementType.Goals.ToList())
            {
                goalRepository.Delete(goal);
            }

            if (measurementType.Owner.PrimaryMeasurementType == measurementType)
                measurementType.Owner.PrimaryMeasurementType = null;
            if (measurementType.Owner.SecondaryMeasurementType == measurementType)
                measurementType.Owner.SecondaryMeasurementType = null;

            Repository.Delete(measurementType);
            Repository.SaveChanges();
        }
    }
}
